﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sharp.Interprocess.MemoryManager;
using System;
using System.Collections.Generic;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Text;
using FluentAssertions;

namespace Sharp.Interprocess.Tests
{
    [TestClass]
    public class HelperTests
    {
        #region Constants
        private const string TEST_MAP_NAME = "StoredObjectTest";
        private const int TEST_STORAGE_SIZE = 1024;
        #endregion

        #region Test Object Definitions

        private class TestObjectWithId
        {
            public int Id { get; set; }

            public int Num { get; set; }

            public string Str { get; set; }

            public override int GetHashCode()
            {
                return Num.GetHashCode() ^ Str.GetHashCode() ^ Id.GetHashCode();
            }

            public override bool Equals(object obj)
            {
                var testObj = obj as TestObjectWithId;
                return testObj != null && testObj.Id == Id;
            }
        }

        private class TestObject
        {
            public int Num { get; set; }

            public string Str { get; set; }

            public override int GetHashCode()
            {
                return Num.GetHashCode() ^ Str.GetHashCode();
            }

            public override bool Equals(object obj)
            {
                var testObj = obj as TestObject;
                return testObj != null && testObj.Num == Num && testObj.Str == Str;
            }
        }

        #endregion

        [TestMethod]
        public void MemoryMappedFileStore_AllocatedAndSave_ParameterNonPrimitiveAndNull_ExceptionThrown()
        {
            using (BuddyMemoryManager memoryManager = new BuddyMemoryManager(TEST_MAP_NAME + "_MemoryManager", TEST_STORAGE_SIZE))
            {
                using (var memoryMappedFileStore = new MemoryMappedFileStore(memoryManager))
                {
                    memoryMappedFileStore.Invoking(store => store.AllocateAndSave((TestObject)null))
                                         .ShouldThrow<ArgumentException>();
                }
            }
        }

        [TestMethod]
        public void MemoryMappedFileStore_AllocatedAndSave_ParameterPrimitiveAndDefaultValue_ExceptionNotThrown()
        {
            using (BuddyMemoryManager memoryManager = new BuddyMemoryManager(TEST_MAP_NAME + "_MemoryManager", TEST_STORAGE_SIZE))
            {
                using (var memoryMappedFileStore = new MemoryMappedFileStore(memoryManager))
                {
                    memoryMappedFileStore.Invoking(store => store.AllocateAndSave<float>(default(float)))
                                         .ShouldNotThrow<ArgumentException>();
                }
            }
        }


        [TestMethod]
        public void MemoryMappedFileStore_SaveLoadPrimitive_SaveLoadSucceeded()
        {
            using (BuddyMemoryManager memoryManager = new BuddyMemoryManager(TEST_MAP_NAME + "_MemoryManager", TEST_STORAGE_SIZE))
            {
                using (var memoryMappedFileStore = new MemoryMappedFileStore(memoryManager))
                {
                    int x = 123;
                    var segmentHandleOfX = memoryMappedFileStore.AllocateAndSave(x);
                    int fetchedX;
                    var operationSuccess = memoryMappedFileStore.TryLoad(segmentHandleOfX,out fetchedX);
                    operationSuccess.Should().BeTrue();
                    fetchedX.Should().Be(x);
                }
            }
        }

        [TestMethod]
        public void MemoryMappedFileStore_SaveLoadObject_SaveLoadSucceeded()
        {
            using (BuddyMemoryManager memoryManager = new BuddyMemoryManager(TEST_MAP_NAME + "_MemoryManager", TEST_STORAGE_SIZE))
            {
                using (var memoryMappedFileStore = new MemoryMappedFileStore(memoryManager))
                {
                    var testObject = new TestObject() { Num = 1234, Str = "ABC" };
                    var segmentHandleOfTestObject = memoryMappedFileStore.AllocateAndSave(testObject);
                    
                    TestObject fetchedTetObject = null;
                    var operationSuccess = memoryMappedFileStore.TryLoad(segmentHandleOfTestObject, out fetchedTetObject);

                    operationSuccess.Should().BeTrue();
                    fetchedTetObject.ShouldBeEquivalentTo(testObject);
                }
            }
        }    
    }
}
